<template>
  <div class="h-tooltip" v-on="outerEvents" ref="Htooltip">
    <div class="h-tooltip_trigger" ref="triggerNode" v-on="events">
      <slot></slot>
    </div>
    <Transition :name="transition">
      <div class="h-tooltip_popper" v-show="isShow" ref="popperNode">
        <slot name="content">{{ content }}</slot>
        <div id="arrow" data-popper-arrow></div>
      </div>
    </Transition>
  </div>
</template>

<script setup lang="ts">
import {
  ref,
  watch,
  useTemplateRef,
  reactive,
  onUnmounted,
  computed,
} from "vue";
import type { TooltipProps, TooltipManual, VisibleChange } from "./types";
import type { Instance } from "@popperjs/core";
import { createPopper } from "@popperjs/core";
import useClickOutside from "@/hooks/useClickOutside";
import { debounce } from "lodash";

const emits = defineEmits<VisibleChange>();
const {
  openDelay = 0,
  closeDelay = 0,
  placement = "left",
  trigger = "hover",
  manual,
  optionsPlacement,
  transition = "fade",
} = defineProps<TooltipProps>();
const isShow = ref(false);
const triggerNode = useTemplateRef<HTMLElement>("triggerNode");
const popperNode = useTemplateRef<HTMLElement>("popperNode");
const Htooltip = ref<HTMLElement>();
let popperInstance: Instance | null = null;
let events: Record<string, any> = reactive({});
let outerEvents: Record<string, any> = reactive({});
// const closeNum = ref(0)
// const openNum = ref(0)

const popperOptions = computed(() => {
  return {
    placement,
    modifiers: [
      {
        name: "offset",
        options: {
          offset: [0, 9],
        },
      },
    ],
    ...optionsPlacement,
  };
});

const close = () => {
  // closeNum.value++
  // console.log('close', closeNum.value);

  isShow.value = false;
  emits("visibleChange", false);
};

const open = () => {
  // openNum.value++
  // console.log('open', openNum.value);

  isShow.value = true;
  emits("visibleChange", true);
};

const openDebounce = debounce(open, openDelay);
const closeDebounce = debounce(close, closeDelay);

const openDelayFun = () => {
  closeDebounce.cancel();
  openDebounce();
};

const closeDelayFun = () => {
  openDebounce.cancel();
  closeDebounce();
};

const triggerEventClick = () => {
  if (isShow.value) {
    closeDelayFun();
  } else {
    openDelayFun();
  }
};

const eventFun = () => {
  if (trigger == "click") {
    events["click"] = triggerEventClick;
  } else if (trigger == "hover") {
    events["mousemove"] = openDelayFun;
    outerEvents["mouseleave"] = closeDelayFun;
  }
};

if (!manual) {
  eventFun();
}

useClickOutside(Htooltip, () => {
  if (trigger == "click" && isShow.value && !manual) {
    closeDelayFun();
  }
  if (isShow.value) {
    emits("clickOutside", true);
  }
});

watch(
  () => manual,
  (isManual) => {
    if (isManual) {
      events = {};
      outerEvents = {};
    } else {
      eventFun();
    }
  }
);

watch(
  () => trigger,
  (newValue, oldValue) => {
    if (newValue !== oldValue) {
      events = {};
      outerEvents = {};
      eventFun();
    }
  }
);

watch(
  isShow,
  (newValue) => {
    if (newValue) {
      if (triggerNode.value && popperNode.value) {
        popperInstance = createPopper(
          triggerNode.value,
          popperNode.value,
          popperOptions.value
        );
      } else {
        popperInstance?.destroy();
      }
    }
  },
  { flush: "post" }
);

onUnmounted(() => {
  popperInstance?.destroy();
});

defineExpose<TooltipManual>({
  show: openDelayFun,
  hide: closeDelayFun,
});
</script>

<style scoped>
@import url(./style.css);
</style>
